#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//第21讲：动态内存管理+第22讲：⽂件操作
//7. 总结C/C++中程序内存区域划分
//C/C++程序内存分配的⼏个区域：
//1.内核空间：用户代码不能读写
//2.栈区：栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
//3.堆区（heap）：⼀般由程序员分配释放， 若程序员不释放，程序结束时可能由OS(Operate System操作系统)回收 。分配⽅式类似于链表。
//4.数据段（静态区）：（static）存放全局变量、静态数据。程序结束后由系统释放。
//5.代码段：存放函数体（类成员函数和全局函数）的⼆进制代码。

//1. 为什么使⽤⽂件？
//我们写的程序的数据是存在电脑内存当中的，输入一个数据:scanf("%d",&n),当程序运行完退出后内存回收，我们就找不到原来输入的数据了
//如果要将数据进⾏持久化的保存，我们可以使⽤⽂件（在硬盘上）。

//2. 什么是⽂件？
//磁盘（硬盘）上的⽂件是⽂件。
//但是在程序设计中，我们⼀般谈的⽂件有两种：程序⽂件、数据⽂件（从⽂件功能的⻆度来分类的）。
//程序⽂件：
//程序⽂件包括源程序⽂件（后缀为.c）, ⽬标⽂件（windows环境后缀为.obj）, 可执⾏程序（windows环境后缀为.exe）。
//数据⽂件
//⽂件的内容不⼀定是程序，⽽是程序运⾏时读写的数据，⽐如程序运⾏需要从中读取数据的⽂件，或者输出内容的⽂件。

//在以前各章所处理数据的输⼊输出都是以终端为对象的，即从终端的键盘输⼊数据，运⾏结果显⽰到显⽰器上。
//其实有时候我们会把信息输出到磁盘上，当需要的时候再从磁盘上把数据读取到内存中使⽤，这⾥处理的就是磁盘上⽂件。

//2.3 ⽂件名
//⼀个⽂件要有⼀个唯⼀的⽂件标识，以便⽤⼾识别和引⽤。
//⽂件名包含3部分：⽂件路径 + ⽂件名主⼲ + ⽂件后缀
//例如： c : \code\test.txt
//为了⽅便起⻅，⽂件标识常被称为⽂件名。


//3. ⼆进制⽂件和⽂本⽂件？
//根据数据的组织形式，数据⽂件被称为⽂本⽂件或者⼆进制⽂件。
//数据在内存中以⼆进制的形式存储，如果不加转换的输出到外存的⽂件中，就是⼆进制⽂件。
//如果要求在外存上以ASCII码的形式存储，则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂
//本⽂件。
//⼀个数据在⽂件中是怎么存储的呢？
//字符⼀律以ASCII形式存储，数值型数据既可以⽤ASCII形式存储，也可以使⽤⼆进制形式存储。
//如有整数10000，如果以ASCII码的形式输出到磁盘，则磁盘中占⽤5个字节（每个字符⼀个字节），⽽
//⼆进制形式输出，则在磁盘上只占4个字节。



//4. ⽂件的打开和关闭
//4.1 流和标准流
//4.1.1 流
//我们程序的数据需要输出到各种外部设备，也需要从外部设备获取数据，不同的外部设备的输⼊输出
//操作各不相同，为了⽅便程序员对各种设备进⾏⽅便的操作，我们抽象出了流的概念，我们可以把流
//想象成流淌着字符的河。
//C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
//⼀般情况下，我们要想向流⾥写数据，或者从流中读取数据，都是要打开流，然后操作。

//4.1.2 标准流
//那为什么我们从键盘输⼊数据，向屏幕上输出数据，并没有打开流呢？
//那是因为C语⾔程序在启动的时候，默认打开了3个流：
//• stdin - 标准输⼊流，在⼤多数的环境中从键盘输⼊，scanf函数就是从标准输⼊流中读取数据。
//• stdout - 标准输出流，⼤多数的环境中输出⾄显⽰器界⾯，printf函数就是将信息输出到标准输出
//流中。
//• stderr - 标准错误流，⼤多数环境中输出到显⽰器界⾯。
//这是默认打开了这三个流，我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
//stdin、stdout、stderr 三个流的类型是： FILE* ，通常称为⽂件指针。
//C语⾔中，就是通过 FILE* 的⽂件指针来维护流的各种操作的。


//4.2 ⽂件指针
//缓冲⽂件系统中，关键的概念是“⽂件类型指针”，简称“⽂件指针”。
//每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区，⽤来存放⽂件的相关信息（如⽂件的名
//字，⽂件状态及⽂件当前的位置等）。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系
//统声明的，取名 FILE.
//不同的C编译器的FILE类型包含的内容不完全相同，但是⼤同⼩异。
//每当打开⼀个⽂件的时候，系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量，并填充其中的信
//息，使⽤者不必关⼼细节。
//每一个被打开的文件都有一个文件信息区和指向这个文件信息区的文件指针FILE指针
//⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量，这样使⽤起来更加⽅便。
//下⾯我们可以创建⼀个FILE* 的指针变量:
//FILE * pf;//⽂件指针变量


//4.3 ⽂件的打开和关闭
//⽂件在读写之前应该先打开⽂件，在使⽤结束之后应该关闭⽂件。
//在编写程序的时候，在打开⽂件的同时，都会返回⼀个FILE* 的指针变量指向该⽂件，也相当于建⽴了
//指针和⽂件的关系。
//ANSI C 规定使⽤ fopen 函数来打开⽂件， fclose 来关闭⽂件。
//FILE* fopen(const char* filename, const char* mode);
//filename:文件名
//mode:打开方式
//关闭⽂件
//int fclose(FILE* stream);

//下⾯都是⽂件的打开模式：
//文件使用方式:            含义:						如果指定⽂件不存在
// “r”（只读） 为了输⼊数据，打开⼀个已经存在的⽂本⽂件		出错
// "w”（只写） 为了输出数据，打开⼀个⽂本⽂件			建⽴⼀个新的⽂件
// “a”（追加） 向⽂本⽂件尾添加数据					建⽴⼀个新的⽂件
//注意：当指定文件里面有数据的话，选择"w"会把原来的数据覆盖掉！
//以读或者写的形式打开文件:
//int main()
//{
//	//打开文件:
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)//如果当前路径没有test.txt的话就返回空指针
//	{
//		perror("fopen");
//		return 1;
//	}
//	//读文件:
//	//.....
//
//	//关闭文件:
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}



//5. ⽂件的顺序读写
//5.1 顺序读写函数介绍
//第一组:fputc:字符输出函数
//int fputc(int character, FILE* stream);
//character:你打算输出的字符
//stream:指向文件的指针
//返回值：是你成功打印的那个字符的ASCII码值
//int main()
//{
//	//打开文件
//	FILE*pf=fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//向文件里面写（输出）
//	for (int i = 'a'; i <= 'z'; i++)
//	{
//		fputc(i, pf);
//	}
//	//关闭文件:
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//fgetc:⽂本⾏输⼊函数
//int fgetc ( FILE * stream );
//stream:指向文件的指针
//返回的是你成功读取到的字符的ascii码值
//如果stream在文件末尾或者发生读写错误就返回EOF(-1)
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//输入:
//	int ch = 0;
//	while ((ch = fgetc(pf)) != EOF)
//	{
//		printf("%c", ch);
//	}
//	//关闭
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}


//第二组：fputs：⽂本⾏输出函数
//int fputs(const char* str, FILE* stream);
//不会写\0
//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	fputs("hello world\n", pf);
//	fputs("i love you~\n", pf);
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//fgets：⽂本⾏输⼊函数
//char * fgets ( char * str, int num, FILE * stream );
//从stream里读数据，最多读num个字节，最终把读到的内容放在str指向的空间里
//由于会为\0留一个位置，假设num为10的话，只能读9个数据，最后一个位置要放\0
//只能读一行的数据，不会换行读取，如果可以读到一行的末尾，也会把\n读进去
//如果读入成功的话，返回值就是str,如果读取失败（遇到文件末尾或者读取错误，就返回NULL）
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	char arr[20] = "xxxxxxxxxxxxxxxxxxxx";
//	fgets(arr, 15, pf);//会在第末尾置放\0，把hello world\n这12个字符全部读入（包括\n）最后再填1个\0
//	printf("%s", arr);
//	fgets(arr, 15, pf);
//	printf("%s", arr);
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//由于读取成功返回的是str，读取到末尾返回的是NULL,所以也可以这样写:
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	char arr[20] = "xxxxxxxxxxxxxxxxxxxx";
//	while (fgets(arr, 2, pf) != NULL)
//	{
//		printf("%s", arr);
//	}
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
